package com.workingpub.commons.orm.internal;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.persistence.JoinTable;

import org.springframework.util.StringUtils;

import com.workingpub.commons.domain.Identifiable;
import com.workingpub.commons.orm.SqlBuildEntry;

public class UpdateBuilder extends AbstractSqlBuilder {

	public <T extends Identifiable<?>> UpdateBuilder(Class<T> type) {
		super(type);
	}

	@Override
	protected <T extends Identifiable<?>> SqlBuildEntry build(EntityMetaInfo metaInfo, T entity) throws Exception {
		SqlBuildEntry sqlEntry = new SqlBuildEntry();
		sqlEntry.append("Update " + metaInfo.getTableName()).append(" Set ");
		boolean first = true;
		for (String column : metaInfo.getColumnInfos().keySet()) {
			if (metaInfo.getIdname().equalsIgnoreCase(column)) {
				continue;
			}
			if (first) {
				sqlEntry.append(column).append(" = :" + column);
				first = false;
			} else {
				sqlEntry.append(" ,").append(column).append(" = :" + column);
			}
			sqlEntry.putParam(column, convertValue(metaInfo.getColumnInfos().get(column), entity));
		}
		sqlEntry.append(" where " + metaInfo.getIdname()).append(" = :" + metaInfo.getIdname());
		sqlEntry.putParam(metaInfo.getIdname(), entity.getId());

		return sqlEntry;
	}

	@Override
	protected <T extends Identifiable<?>> List<SqlBuildEntry> buildManyToMany(Field field, T entity) throws Exception {
		JoinTable joinTable = field.getAnnotation(JoinTable.class);
		return buildManyToMany(joinTable, field, entity);
	}

	@Override
	protected <T extends Identifiable<?>> List<SqlBuildEntry> buildManyToMany(PropertyDescriptor property, T entity)
			throws Exception {
		JoinTable joinTable = property.getReadMethod().getAnnotation(JoinTable.class);
		return buildManyToMany(joinTable, property, entity);
	}

	private <T extends Identifiable<?>> List<SqlBuildEntry> buildManyToMany(JoinTable joinTable, Object value, T entity)
			throws Exception {
		if (StringUtils.isEmpty(entity.getId())) {
			logger.warn("Entity id is null, build failed.");
			return null;
		}
		String tableName = joinTable.name();
		String joinColumn = joinTable.joinColumns()[0].name();
		String inverseColumn = joinTable.inverseJoinColumns()[0].name();

		List<SqlBuildEntry> sqlEntries = new ArrayList<SqlBuildEntry>();
		SqlBuildEntry removeEntry = new SqlBuildEntry();
		removeEntry.append("Delete From " + tableName).append(" Where ").append(joinColumn).append(" = :" + joinColumn);
		removeEntry.putParam(joinColumn, entity.getId());
		sqlEntries.add(removeEntry);

		Collection<?> values = convertValues(value, entity);
		if (null != values && !values.isEmpty()) {
			for (Object entry : values) {
				Identifiable<?> domainEntry = Identifiable.class.cast(entry);
				if (null == domainEntry) {
					continue;
				}
				SqlBuildEntry buildEntry = new SqlBuildEntry();
				buildEntry.append("Insert into " + tableName).append("(" + joinColumn).append(" ," + inverseColumn)
						.append(") values (").append(":" + joinColumn).append(" ,:" + inverseColumn).append(")");
				buildEntry.putParam(joinColumn, entity.getId()).putParam(inverseColumn, domainEntry.getId());
				sqlEntries.add(buildEntry);
			}
		}
		return sqlEntries;
	}

}
